package com.momoc.mrpc.rpc;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.momoc.mrpc.annotation.MRpcReference;
import com.momoc.mrpc.annotation.MRpcService;
import com.momoc.mrpc.register.RegisterContext;
import com.momoc.mrpc.register.definition.MRpcRegDefinition;
import com.momoc.mrpc.rpc.client.ClientContext;
import com.momoc.mrpc.rpc.handler.MRpcResponseHandler;
import com.momoc.mrpc.rpc.message.MRpcRequestMessage;
import com.momoc.mrpc.rpc.message.MRpcResponseMessage;
import com.momoc.mrpc.util.SequenceIdGenerator;
import io.netty.channel.Channel;
import io.netty.util.concurrent.DefaultPromise;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

/**
 * @author momoc
 * @version 1.0
 * @className MRpcReferenceInject
 * @description
 * @date 2022/7/26 10:51 上午
 */
@Slf4j
@Component
public class MRpcReferenceInject implements BeanPostProcessor {

    @Autowired
    ClientContext clientContext;
    @Autowired
    private ApplicationContext applicationContext;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    Random random = new Random();
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class aClass = bean.getClass();
        for (Field declaredField : aClass.getDeclaredFields()) {
            MRpcReference annotation = declaredField.getAnnotation(MRpcReference.class);
            if (annotation != null){
                String name = declaredField.getType().getName();
                MRpcRegDefinition mRpcRegDefinition = RegisterContext.rpcNameServiceMap.get(name);
                if (mRpcRegDefinition == null){
                    continue;
                }
                try {
                    Object proxyObj = Proxy.newProxyInstance(bean.getClass().getClassLoader(), new Class[]{mRpcRegDefinition.getInterfaceClass()}, (proxy,  method,  args) -> {
                        log.info("rpc:{}, {}, {}", proxy.getClass(), method, args);

                        /**
                         *  Object 提供的 toString、hashCode 等方法是不需要代理执行的，所以添加 Object.class.equals(method.getDeclaringClass()) 判断。
                         */
                        if (Object.class.equals(method.getDeclaringClass())) {
                            return proxy;
                        }
//                        if (method.getName().contains("toString")){
//
//                        }
                        String interfaceClassName = mRpcRegDefinition.getInterfaceClass().getName();
                        //发送rpc远程调用请求
                        List<Instance> onlineServerByClassName = clientContext.getOnlineServerByClassName(interfaceClassName);
                        int r = random.nextInt(onlineServerByClassName.size());
                        Instance instance = onlineServerByClassName.get(r);
                        Channel channel = clientContext.doOpen(instance.getIp(), instance.getPort());
                        MRpcRequestMessage message = new MRpcRequestMessage();
                        message.setInterfaceName(interfaceClassName);
                        message.setMethodName(method.getName());
                        message.setReturnType(method.getReturnType());
                        message.setParameterTypes(method.getParameterTypes());
                        message.setParameterValue(args);
                        int sequenceId = SequenceIdGenerator.nextId();
                        message.setSequenceId(sequenceId);
                        channel.writeAndFlush(message);
                        // 3. 准备一个空 Promise 对象，来接收结果             指定 promise 对象异步接收结果线程
                        DefaultPromise<Object> promise = new DefaultPromise<>(channel.eventLoop());
                        MRpcResponseHandler.PROMISES.put(message.getSequenceId(), promise);
                        // 4. 等待 promise 结果
                        promise.await();
                        if(promise.isSuccess()) {
                            Object now = promise.getNow();
                            // 调用正常
                            return now;
                        } else {
                            // 调用失败
                            throw new RuntimeException(promise.cause().getMessage());
                        }

                    });
                    declaredField.setAccessible(true);
                    //拿到有注解的成员变量，对对他进行注入
                    declaredField.set(bean,  proxyObj);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
        //进行反射包装
        return bean;
    }
}
